﻿using System.Reflection;

namespace AbpCodeGenerater.Core
{
    public static class TemplateModelExtension
    {
        public static ModelDefine? ToModelDefine(this PropertyInfo property)
        {
            if (!property.GetAccessors().Any(mi => mi.IsStatic)) return null;

            var propertyName = property.Name;

            if (propertyName.EndsWith("MaxLength"))
            {
                return new ModelDefine { Name = propertyName.Replace("MaxLength", ""), Func = "HasMaxLength", ValueProperty = propertyName };
            }
            else if (propertyName.EndsWith("ColumnType"))
            {
                return new ModelDefine { Name = propertyName.Replace("ColumnType", ""), Func = "HasColumnType", ValueProperty = propertyName };
            }
            else if (propertyName.EndsWith("DefaultValue"))
            {
                return new ModelDefine { Name = propertyName.Replace("DefaultValue", ""), Func = "HasDefaultValue", ValueProperty = propertyName };
            }
            else if (propertyName.EndsWith("ColumnName"))
            {
                return new ModelDefine { Name = propertyName.Replace("ColumnName", ""), Func = "HasColumnName", ValueProperty = propertyName };
            }
            else if (propertyName.EndsWith("Required"))
            {
                return new ModelDefine { Name = propertyName.Replace("Required", ""), Func = "IsRequired", ValueProperty = propertyName };
            }
            else
            {
                return null;
            }
        }

        public static bool IsNavigationProperty(this PropertyInfo property)
        {
            return !property.IsValueProperty() && !property.IsArrayProperty();
        }

        public static bool IsValueProperty(this PropertyInfo property)
        {
            return property.PropertyType == typeof(string) || property.PropertyType.IsValueType;
        }

        public static bool IsArrayProperty(this PropertyInfo property)
        {
            return property.PropertyType != typeof(string)
                && property.PropertyType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
        }

        public static string ToDeclareText(this Type type)
        {
            if (type.Name == typeof(string).Name) return "string";
            if (type.Name == typeof(char).Name) return "char";
            if (type.Name == typeof(bool).Name) return "bool";
            if (type.Name == typeof(short).Name) return "short";
            if (type.Name == typeof(int).Name) return "int";
            if (type.Name == typeof(long).Name) return "long";
            if (type.Name == typeof(ushort).Name) return "ushort";
            if (type.Name == typeof(uint).Name) return "uint";
            if (type.Name == typeof(ulong).Name) return "ulong";
            if (type.Name == typeof(double).Name) return "double";
            if (type.Name == typeof(float).Name) return "float";
            if (type.Name == typeof(decimal).Name) return "decimal";
            if (type.Name == typeof(Nullable<>).Name) return $"{type.GenericTypeArguments[0].ToDeclareText()}?";
            if (type.IsGenericType)
            {
                return $"{type.Name.TrimEnd('1').TrimEnd('`')}<{string.Join(", ", type.GenericTypeArguments.Select(x => x.ToDeclareText()))}>";
            }

            return type.Name;
        }

        public static string ToDtoDeclareText(this Type type)
        {
            if (type.Name == typeof(string).Name) return "string";
            if (type.Name == typeof(char).Name) return "char";
            if (type.Name == typeof(bool).Name) return "bool";
            if (type.Name == typeof(short).Name) return "short";
            if (type.Name == typeof(int).Name) return "int";
            if (type.Name == typeof(long).Name) return "long";
            if (type.Name == typeof(ushort).Name) return "ushort";
            if (type.Name == typeof(uint).Name) return "uint";
            if (type.Name == typeof(ulong).Name) return "ulong";
            if (type.Name == typeof(double).Name) return "double";
            if (type.Name == typeof(float).Name) return "float";
            if (type.Name == typeof(decimal).Name) return "decimal";
            if (type.Name == typeof(Nullable<>).Name) return $"{type.GenericTypeArguments[0].ToDtoDeclareText()}?";
            if (type.IsGenericType)
            {
                return $"List<{string.Join(", ", type.GenericTypeArguments.Select(x => x.ToDtoDeclareText()))}>";
            }

            if (type == typeof(string) || type.IsValueType)
            {
                return type.Name;
            }
            return type.Name + "Dto";
        }

        public static List<PropertyInfo> ExcludePropertys(this List<PropertyInfo> properties, params string[] excludeProperty)
        {
            return properties.Where(x => !excludeProperty.Contains(x.Name)).ToList();
        }

        public static List<PropertyInfo> ExcludeExtensiblePropertys(this List<PropertyInfo> properties)
        {
            return properties.ExcludePropertys("Id", "ExtraProperties");
        }

        public static List<PropertyInfo> ExcludeConcurrencyStamp(this List<PropertyInfo> properties)
        {
            return properties.ExcludePropertys("ConcurrencyStamp");
        }

        public static List<string> GetUsings(this Type type)
        {
            var usings = new List<string>();
            if (type == typeof(string) || type.IsValueType) return usings;

            if (!string.IsNullOrEmpty(type.Namespace))
            {
                usings.Add(type.Namespace);
            }

            if (type.IsGenericType)
            {
                foreach (Type genericTypeArgument in type.GenericTypeArguments)
                {
                    usings.AddRange(genericTypeArgument.GetUsings());
                }
            }

            return usings;
        }

        public static List<string> ExcludeUsings(this List<string> usings, params string[] excludeUsing)
        {
            return usings.Where(x => !excludeUsing.Contains(x)).ToList();
        }
    }
}
